package com.gitee.wsgalaxy.gnodroid.connect;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.gitee.wsgalaxy.gnodroid.module.GndModule;

/**
 * Created by wsgalaxy on 3/16/18.
 */

public class GndConnect implements Runnable {

    int port = 4396;
    Thread listen_thread = null;
    Thread module_thread = null;
    public Handler module_handle = null;
    Selector selector= null;
    ServerSocketChannel server_socket_channel = null;
    ArrayList<GndSession> session_list = new ArrayList<GndSession>();
    //ArrayList<GndModule> modules_list = new ArrayList<GndModule>();


    GndConnect(int p) {
        port = p;
        /*
        try {
            selector = Selector.open();
            server_socket_channel = ServerSocketChannel.open();
            //serverSocketChannel.bind(new InetSocketAddress(InetAddress.getLocalHost(), port));
            ServerSocket serverSocket = server_socket_channel.socket();
            serverSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), port));
            server_socket_channel.configureBlocking(false);
        } catch (IOException e) {
            Log.d("gnd", e.getMessage());
        }
        */
    }

    /**
     * 建立一个新的连接
     * @param p
     * @return
     */
    static public GndConnect getConnect(int p)
    {
        GndConnect connect = new GndConnect(p);
        /*
        if (connect.selector == null || connect.server_socket_channel == null) {
            return null;
        } else {
            return connect;
        }
        */
        return connect;
    }


    /**
     * 将通道挂到nio上监听
     * @param channel
     * @param op
     * @return
     */
    public boolean attachChannel(SocketChannel channel, int op)
    {
        try {
            channel.configureBlocking(false);
            channel.register(selector,op);
        } catch (Exception e) {
            Log.d("gnd", "error" + e.getMessage());
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /*
    public boolean addModule(GndModule gndModule) {
        //return modules_list.add(gndModule);
    }
    */

    /**
     * 启动连接，包括启动连接线程和模块运行线程
     */
    public void start()
    {
        if (listen_thread != null) {
            return;
        }

        //启动监听线程
        //this.port = port;
        listen_thread = new Thread(this);
        listen_thread.start();

        //启动模块线程
        module_thread = new Thread(new GndConnectModuleThread());
        module_thread.start();
    }

    @Override
    /**
     * nio连接线程
     */
    //连接监听线程
    public void run()
    {
        Log.d("gnd", "nio start");
        try {
            //创建服务端的socket
            selector = Selector.open();
            server_socket_channel = ServerSocketChannel.open();
            //serverSocketChannel.bind(new InetSocketAddress(InetAddress.getLocalHost(), port));
            ServerSocket serverSocket = server_socket_channel.socket();
            serverSocket.bind(new InetSocketAddress("127.0.0.1", port));
            server_socket_channel.configureBlocking(false);

            startSelect();
        } catch (Exception e) {
            Log.d("gnd", "thread start fail" + e.getMessage());
            e.printStackTrace();
            listen_thread = null;
        }
    }

    /**
     * nio连接循环
     * @throws ClosedChannelException
     * @throws IOException
     * @throws Exception
     */
    void startSelect() //throws ClosedChannelException, IOException, Exception
    {
        Log.d("gnd", "nio running");

        try {
            server_socket_channel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (ClosedChannelException e) {
            Log.d("gnd", e.getMessage());
            e.printStackTrace();
        }

        try {
            while (true) {
                int nkeys = selector.select(100);
                if (nkeys > 0) {
                    //有连接
                    Log.d("gnd", "connect accept");
                    Set selectKeys = selector.selectedKeys();
                    Iterator i = selectKeys.iterator();

                    while (i.hasNext()) {
                        SelectionKey k = (SelectionKey) i.next();
                        if (k.isAcceptable()) {
                            //有新的连接socket需要建立
                            Log.d("gnd", "acceptable");
                            //为该连接创建一个session
                            GndSession session = new GndSession(((ServerSocketChannel) k.channel()).accept(),
                                    this);
                            session_list.add(session);
                        } else {
                            Log.d("gnd", "readable");
                            //有新数据可读
                            SocketChannel socketChannel = (SocketChannel) k.channel();
                            //获取与该连接匹配的session
                            GndSession matchedSession = getSession(socketChannel);
                            if (matchedSession == null) {
                                throw new Exception("session null");
                            } else {
                                matchedSession.selectionKeyGot(k);
                            }
                        }

                        i.remove();
                    }
                }
            }
        } catch (Exception e) {
            Log.d("gnd", "error: " + e.getMessage());
            e.printStackTrace();
        }
    }

    //根据挂在nio上的channel获取绑定的session
    GndSession getSession(SocketChannel channel)
    {
        for (GndSession s : session_list) {
            if (s.checkChannel(channel)) {
                return s;
            }
        }
        return null;
    }

    /*
    GndModule getModule(String mid)
    {
        return null;
    }
    */

    /**
     * 模块运行的线程
     */
    class GndConnectModuleThread implements Runnable
    {
        public void run()
        {
            Log.d("gnd", "module start");
            Looper.prepare();
            module_handle = new Handler();
            Looper.loop();
        }
    }
}
